/* eslint-disable @typescript-eslint/no-var-requires */
import Koa from 'koa';
import { decodeToken, getToken, secret } from '../util/token';
const jwt = require('jsonwebtoken');
const unless = require('koa-unless');

// token自动校验和自动刷新中间件
module.exports = (): Koa.Middleware<
  Koa.DefaultState,
  Koa.DefaultContext
> => {
  const middleware = (ctx: Context, next: Next): Promise<void> => {
    if (ctx.header && ctx.header.authorization) {
      const parts = ctx.header.authorization.split(' ');
      if (parts.length === 2) {
        //取出token
        const scheme = parts[0];
        const token = parts[1];
  
        // 获取完整的token信息，因为这里也可能报错，所以都放在try里面,这里不需要头部和签名信息
        const tokenInfo = decodeToken(token);
        if (/^Bearer$/i.test(scheme)) {
          try {
            //jwt.verify方法验证token是否有效
            jwt.verify(token, secret, {
              complete: true
            });
          } catch (error) {
            switch (error.name) {
              // 过期了
              case 'TokenExpiredError':
                if (Object.keys(tokenInfo).length) {
                  // 这里有坑，要去掉载荷中的时间
                  delete tokenInfo.iat;
                  delete tokenInfo.exp;
                  //将新token放入Authorization中返回给前端
                  ctx.res.setHeader('Authorization', getToken(tokenInfo));
                } else {
                  // 其它情况都返回401,交给jwt-token统一处理
                  ctx.throw(401, 'Authentication Error');
                }
                break;
              // token有误
              case 'JsonWebTokenError':
                // 这个错误不爆露给前端了，后台留着上报日志
                console.log(error.message, 'JsonWebTokenError');
                ctx.throw(401, 'Authentication Error');
                break;
              default:
                ctx.throw(401, 'Authentication Error');
            }
          }
        } else {
          ctx.throw(401, 'Authentication Error');
        }
      } else {
        ctx.throw(401, 'Bearer error');
      }
    } else {
      ctx.throw(401, 'Authentication Error');
    }
  
    return next()
  };
  middleware.unless = unless;
  return middleware;
}